package com.atguigu.tingshu.order.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.account.AccountFeignClient;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.model.order.OrderDerate;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.order.helper.SignHelper;
import com.atguigu.tingshu.order.mapper.OrderInfoMapper;
import com.atguigu.tingshu.order.pattern.TradeStrategy;
import com.atguigu.tingshu.order.pattern.factory.TradeStrategyFactory;
import com.atguigu.tingshu.order.service.AccountPayBizService;
import com.atguigu.tingshu.order.service.OrderDerateService;
import com.atguigu.tingshu.order.service.OrderDetailService;
import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.order.OrderDerateVo;
import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service
@RefreshScope // 动态刷新
@SuppressWarnings({"all"})
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private UserFeignClient userFeignClient;

    @Autowired
    private AccountFeignClient accountFeignClient;


    @Autowired
    private AlbumFeignClient albumFeignClient;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private OrderDetailService orderDetailService;

    @Autowired
    private OrderDerateService orderDerateService;

    @Autowired
    private TradeStrategyFactory tradeStrategyFactory;

    @Autowired
    private RabbitService rabbitService;

    /**
     * 订单结算（VIP套餐、专辑、声音三种商品结算）
     *
     * @return
     */
    @Override
    public OrderInfoVo trade(TradeVo tradeVo, Long userId) {
        //1.根据付款项目类型获取对应的策略实现类对象
        TradeStrategy tradeStrategy = tradeStrategyFactory.getTradeStrategy(tradeVo.getItemType());
        //2.调用不同策略实现类对象进行订单结算
        return tradeStrategy.trade(tradeVo, userId);

       /* //1.创建订单VO对象，创建初始三个金额，两个集合（商品、优惠）
        OrderInfoVo orderInfoVo = new OrderInfoVo();
        //1.1 初始化原价金额 TODO 必须是BigDecimal类型且必须是字符串
        BigDecimal originalAmount = new BigDecimal("0.00");
        //1.2 初始化订单金额
        BigDecimal orderAmount = new BigDecimal("0.00");
        //1.3 初始化减免金额
        BigDecimal derateAmount = new BigDecimal("0.00");

        //1.4. 初始化商品列表集合
        List<OrderDetailVo> orderDetailVoList = new ArrayList<>();
        //1.5. 初始化商品优惠集合
        List<OrderDerateVo> orderDerateVoList = new ArrayList<>();

        //1.6 获取购买项目类型 付款项目类型: 1001-专辑 1002-声音 1003-vip会员
        String itemType = tradeVo.getItemType();

        //2. 处理购买项目类型：VIP套餐
        if (SystemConstant.ORDER_ITEM_TYPE_VIP.equals(itemType)) {
            //2.1 远程调用用户服务得到VIP套餐信息
            VipServiceConfig vipServiceConfig = userFeignClient.getVipServiceConfig(tradeVo.getItemId()).getData();
            Assert.notNull(vipServiceConfig, "VIP套餐{}不存在", tradeVo.getItemId());
            //2.2 计算原金额、订单金额、减免金额
            originalAmount = vipServiceConfig.getPrice();
            orderAmount = vipServiceConfig.getDiscountPrice();
            //2.3 封装商品信息列表
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(tradeVo.getItemId());
            orderDetailVo.setItemName("VIP套餐：" + vipServiceConfig.getName());
            orderDetailVo.setItemUrl(vipServiceConfig.getImageUrl());
            orderDetailVo.setItemPrice(originalAmount);
            orderDetailVoList.add(orderDetailVo);

            //2.4 如果存在优惠，封装优惠列表
            if (originalAmount.compareTo(orderAmount) == 1) {
                derateAmount = originalAmount.subtract(orderAmount);
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_VIP_SERVICE_DISCOUNT);
                orderDerateVo.setDerateAmount(derateAmount);
                orderDerateVo.setRemarks("VIP套餐限时优惠:" + derateAmount);
                orderDerateVoList.add(orderDerateVo);
            }

        } else if (SystemConstant.ORDER_ITEM_TYPE_ALBUM.equals(itemType)) {
            //3. 处理购买项目类型：专辑
            //3.1 远程调用用户服务，判断是否重复购买专辑，如果已购过，则业务终止
            Long albumId = tradeVo.getItemId();
            Boolean flag = userFeignClient.isPaidAlbum(albumId).getData();
            Assert.isFalse(flag, "用户已购买专辑{}", albumId);
            //3.2 远程调用专辑服务，获取专辑价格以及折扣（普通用户折扣，VIP会员折扣）
            AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(albumId).getData();
            Assert.notNull(albumInfo, "专辑{}不存在", albumId);
            BigDecimal price = albumInfo.getPrice();
            BigDecimal discount = albumInfo.getDiscount();
            BigDecimal vipDiscount = albumInfo.getVipDiscount();

            //3.3 远程调用用户服务，获取用户身份，是否为VIP
            Boolean isVIP = false;
            UserInfoVo userInfoVo = userFeignClient.getUserInfoVo(userId).getData();
            Assert.notNull(userInfoVo, "用户{}不存在", userId);
            if (userInfoVo.getIsVip().intValue() == 1
                    && userInfoVo.getVipExpireTime().after(new Date())) {
                isVIP = true;
            }

            //3.4 计算相关价格
            //3.4.1 暂时将订单价=原价
            originalAmount = price;
            orderAmount = originalAmount;
            //3.4.2 如果是普通用户，且存在普通用户折扣，则订单价=原价 100 *折扣 6
            if (!isVIP && discount.doubleValue() != -1) {
                orderAmount = originalAmount.multiply(discount)
                        .divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP);
            }
            //3.4.3 如果是VIP会员，且存在VIP用户折扣，则订单价=原价*折扣
            if (isVIP && vipDiscount.doubleValue() != -1) {
                orderAmount = originalAmount.multiply(vipDiscount)
                        .divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP);
            }

            //3.5 封装商品信息列表
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(albumId);
            orderDetailVo.setItemName("专辑：" + albumInfo.getAlbumTitle());
            orderDetailVo.setItemUrl(albumInfo.getCoverUrl());
            orderDetailVo.setItemPrice(originalAmount);
            orderDetailVoList.add(orderDetailVo);

            //3.6 封装商品优惠列表
            if (originalAmount.compareTo(orderAmount) == 1) {
                derateAmount = originalAmount.subtract(orderAmount);
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_ALBUM_DISCOUNT);
                orderDerateVo.setDerateAmount(derateAmount);
                orderDerateVo.setRemarks("专辑折扣减免：" + derateAmount);
                orderDerateVoList.add(orderDerateVo);
            }

        } else if (SystemConstant.ORDER_ITEM_TYPE_TRACK.equals(itemType)) {
            //4.处理购买项目类型：声音
            //4.1 远程调用专辑服务，获取未购买声音列表
            Long trackId = tradeVo.getItemId();
            List<TrackInfo> trackInfoList = albumFeignClient.findWaitBuyTrackList(trackId, tradeVo.getTrackCount()).getData();
            Assert.notNull(trackInfoList, "不存在待结算声音", trackId);

            //4.2 远程调用专辑服务获取，专辑价格（声音单价）声音不支持折扣
            AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(trackInfoList.get(0).getAlbumId()).getData();
            Assert.notNull(albumInfo, "专辑{}不存在", albumInfo.getId());
            BigDecimal price = albumInfo.getPrice();

            //4.3 计算相关价格
            originalAmount = price.multiply(new BigDecimal(trackInfoList.size()));
            orderAmount = originalAmount;

            //4.4 封装订单明细列表
            orderDetailVoList = trackInfoList.stream()
                    .map(trackInfo -> {
                        OrderDetailVo orderDetailVo = new OrderDetailVo();
                        orderDetailVo.setItemId(trackInfo.getId());
                        orderDetailVo.setItemName("声音：" + trackInfo.getTrackTitle());
                        orderDetailVo.setItemUrl(trackInfo.getCoverUrl());
                        orderDetailVo.setItemPrice(price);
                        return orderDetailVo;
                    }).collect(Collectors.toList());

        }

        //5.封装订单VO对象
        //5.1 封装相关价格信息
        orderInfoVo.setOriginalAmount(originalAmount);
        orderInfoVo.setOrderAmount(orderAmount);
        orderInfoVo.setDerateAmount(derateAmount);
        //5.2 封装商品相关集合
        orderInfoVo.setOrderDetailVoList(orderDetailVoList);
        orderInfoVo.setOrderDerateVoList(orderDerateVoList);
        //5.3  封装其他信息：流水号、时间戳及签名、项目类型等
        //5.3.1 购买项目类型
        orderInfoVo.setItemType(itemType);
        //5.3.2 流水号机制，防止订单重复提交（1.用户网络卡连续点击结算按钮2.成功提交订单，回退到订单确认页再次提交）
        //5.3.2.1 构建当前用户当前订单流水号Key
        String tradeKey = RedisConstant.ORDER_TRADE_NO_PREFIX + userId;
        //5.3.2.2 采用UUID生成本次订单流水号值
        String tradeNo = IdUtil.randomUUID();
        //5.3.2.3 将流水号值保存到Redis中，设置过期时间（5分钟），防止订单重复提交
        redisTemplate.opsForValue().set(tradeKey, tradeNo, RedisConstant.ORDER_TRADE_EXPIRE, TimeUnit.MINUTES);
        //5.3.2.4 为订单VO封装流水号值
        orderInfoVo.setTradeNo(tradeNo);

        //5.3.3 签名机制，时间戳+签名值，防止订单结算页数据被篡改
        //5.3.1 将订单VO转为Map对象，由于无法确定支付方式，固将VO中payWay为空属性排除掉
        orderInfoVo.setTimestamp(System.currentTimeMillis());
        Map<String, Object> map = BeanUtil.beanToMap(orderInfoVo, false, true);
        //5.3.2 调用生成签名工具方法，底层就是Md5,对订单VO中所有属性值进行排序，拼接成字符串+秘钥，进行MD5加密，返回签名值
        String sign = SignHelper.getSign(map);
        orderInfoVo.setSign(sign);
        //6.响应订单VO对象
        return orderInfoVo;*/
    }

    @Autowired
    private AccountPayBizService accountPayBizService;


    @Value("${order.cancel}")
    private int ttl;

    /**
     * 提交订单（付款方式：1.余额 2.微信）
     *
     * @param orderInfoVo
     * @return
     */
    @Override
    //@GlobalTransactional(rollbackFor = Exception.class)
    public Map<String, String> submitOrder(OrderInfoVo orderInfoVo, Long userId) {
        //1.业务校验：验证流水号，防止订单重复提交 采用Lua脚本保证判断删除原子性
        String tradeKey = RedisConstant.ORDER_TRADE_NO_PREFIX + userId;
        //1.1 定义lua脚本
        String luaScript = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                "then\n" +
                "    return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end";
        //1.2 创建脚本对象
        DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(luaScript);
        redisScript.setResultType(Boolean.class);
        //1.3 执行Lua脚本
        Boolean flag = (Boolean) redisTemplate.execute(redisScript, Arrays.asList(tradeKey), orderInfoVo.getTradeNo());
        if (!flag) {
            throw new GuiguException(500, "验证流水号失败");
        }


        //2.业务校验：`验证签名`，防止订单数据被篡改
        //2.1 将订单VO转为Map对象 由于当时加签将payWay排除掉，并未加入到签名中
        Map<String, Object> map = BeanUtil.beanToMap(orderInfoVo);
        map.remove("payWay");
        //2.2 调用验证签名工具方法
        SignHelper.checkSign(map);

        //3.核心业务：保存订单以及订单明细，优惠列表等  -- 此时订单：未支付
        OrderInfo orderInfo = this.saveOrderInfo(orderInfoVo, userId);

        //支付方式：1101-微信 1102-支付宝 1103-账户余额
        String payWay = orderInfoVo.getPayWay();
        //4. 判断支付方式：余额扣减核心业务
        if (SystemConstant.ORDER_PAY_ACCOUNT.equals(payWay)) {
            accountPayBizService.handlerAccountPay(orderInfo);
        }
        //5.TODO 判断支付方式：微信
        if (SystemConstant.ORDER_PAY_WAY_WEIXIN.equals(payWay)) {
            //5.1 发送延迟消息，进行自动关单，无论这比订单是否支付，都需要发送订单ID
            rabbitService.sendDelayMessage(MqConst.EXCHANGE_CANCEL_ORDER, MqConst.ROUTING_CANCEL_ORDER, orderInfo.getId(), ttl);
        }

        //6.响应结果
        return Map.of("orderNo", orderInfo.getOrderNo());
    }


    /**
     * 保存订单相关信息
     *
     * @param orderInfoVo
     * @param userId
     * @return
     */
    @Override
    public OrderInfo saveOrderInfo(OrderInfoVo orderInfoVo, Long userId) {
        //1.保存订单
        //2.1 创建订单对象
        OrderInfo orderInfo = BeanUtil.copyProperties(orderInfoVo, OrderInfo.class);
        //2.2 设置用户ID
        orderInfo.setUserId(userId);
        //2.3 设置订单标题
        List<OrderDetailVo> orderDetailVoList = orderInfoVo.getOrderDetailVoList();
        if (CollUtil.isNotEmpty(orderDetailVoList)) {
            String itemName = orderDetailVoList.get(0).getItemName();
            orderInfo.setOrderTitle(itemName);
        }
        //2.4 订单状态：未支付
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_UNPAID);
        //2.5 设置订单编号 确保全局唯一且趋势递增 形式：当天日期+雪花算法
        String orderNo = DateUtil.today().replaceAll("-", "") + IdUtil.getSnowflakeNextId();
        orderInfo.setOrderNo(orderNo);
        //2.6 保存订单,获取订单ID
        orderInfoMapper.insert(orderInfo);
        Long orderId = orderInfo.getId();

        //2.保存订单明细列表
        if (CollUtil.isNotEmpty(orderDetailVoList)) {
            List<OrderDetail> orderDetailList = orderDetailVoList.stream().map(orderDetailVo -> {
                OrderDetail orderDetail = BeanUtil.copyProperties(orderDetailVo, OrderDetail.class);
                //关联订单ID
                orderDetail.setOrderId(orderId);
                return orderDetail;
            }).collect(Collectors.toList());
            orderDetailService.saveBatch(orderDetailList);
        }
        //3.保存订单减免列表
        List<OrderDerateVo> orderDerateVoList = orderInfoVo.getOrderDerateVoList();
        if (CollUtil.isNotEmpty(orderDerateVoList)) {
            List<OrderDerate> orderDerateList = orderDerateVoList.stream().map(orderDerateVo -> {
                OrderDerate orderDerate = BeanUtil.copyProperties(orderDerateVo, OrderDerate.class);
                orderDerate.setOrderId(orderId);
                return orderDerate;
            }).collect(Collectors.toList());
            orderDerateService.saveBatch(orderDerateList);
        }
        return orderInfo;
    }

    /**
     * 查询订单信息
     *
     * @param orderNo
     * @return
     */
    @Override
    public OrderInfo getOrderInfo(String orderNo) {
        //1.根据订单编号查询订单信息
        OrderInfo orderInfo = orderInfoMapper.selectOne(
                new LambdaQueryWrapper<OrderInfo>()
                        .eq(OrderInfo::getOrderNo, orderNo)
        );
        Long orderInfoId = orderInfo.getId();

        //2.根据订单ID查询订单明细
        List<OrderDetail> orderDetailList = orderDetailService.list(
                new LambdaUpdateWrapper<OrderDetail>()
                        .eq(OrderDetail::getOrderId, orderInfoId)
        );
        orderInfo.setOrderDetailList(orderDetailList);

        //3.根据订单ID查询订单减免列表
        List<OrderDerate> orderDerateList = orderDerateService.list(
                new LambdaQueryWrapper<OrderDerate>()
                        .eq(OrderDerate::getOrderId, orderInfoId)
        );
        orderInfo.setOrderDerateList(orderDerateList);
        return orderInfo;
    }

    /**
     * 分页获取当前用户订单列表
     *
     * @param page
     * @param limit
     * @return
     */
    @Override
    public Page<OrderInfo> findUserOrderPage(Page<OrderInfo> pageInfo, Long userId) {
        //1.方式一：获取当前用户订单列表,然后遍历订单获取每个订单明显列表
        //return orderInfoMapper.findUserOrderPage(pageInfo, userId);
        //2.TODO 方式二：单独分页查询订单，在根据订单ID集合查询订单明细，封装订单明细列表
        Page<OrderInfo> infoPage = orderInfoMapper
                .selectPage(pageInfo,
                        new LambdaQueryWrapper<OrderInfo>().eq(OrderInfo::getUserId, userId)
                                .select(OrderInfo::getId, OrderInfo::getOrderNo, OrderInfo::getOrderStatus, OrderInfo::getPayWay, OrderInfo::getOriginalAmount, OrderInfo::getOrderAmount, OrderInfo::getDerateAmount)
                                .orderByDesc(OrderInfo::getId)
                );

        //3.获取订单ID集合
        List<Long> orderIdList = infoPage.getRecords().stream().map(OrderInfo::getId).collect(Collectors.toList());

        //4.查询所有订单明细列表
        List<OrderDetail> orderDetailList = orderDetailService.list(
                new LambdaQueryWrapper<OrderDetail>()
                        .in(OrderDetail::getOrderId, orderIdList)
                        .select(OrderDetail::getOrderId, OrderDetail::getItemId, OrderDetail::getItemName, OrderDetail::getItemPrice, OrderDetail::getItemUrl)
        );
        //5.将所有订单明细列表转为Map<Long, List<OrderDetail>> Key为订单ID Value：订单明细列表
        Map<Long, List<OrderDetail>> orderDetailListMap =
                orderDetailList.stream().collect(Collectors.groupingBy(OrderDetail::getOrderId));
        for (OrderInfo record : infoPage.getRecords()) {
            record.setOrderDetailList(orderDetailListMap.get(record.getId()));
        }
        return infoPage;
    }

    /**
     * 检查订单支付状态，如果未支付则关闭订单
     *
     * @param orderId
     */
    @Override
    public void cancelOrder(Long orderId) {
        //1.查询订单对象
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
        //2.判断订单支付状态，如果是未支付将订单关闭
        String orderStatus = orderInfo.getOrderStatus();
        if (SystemConstant.ORDER_STATUS_UNPAID.equals(orderStatus)) {
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_CANCEL);
            orderInfoMapper.updateById(orderInfo);
            //TODO 关闭第三方支付平台（微信支付）支付订单
        }
    }

    /**
     * 用户支付成功后，更新订单状态以及完成权益发放
     *
     * @param orderNo
     */
    @Override
    public void orderPaySuccess(String orderNo) {
        //1.根据订单编号查询订单信息
        //OrderInfo orderInfo = orderInfoMapper.selectOne(
        //        new LambdaQueryWrapper<OrderInfo>()
        //                .eq(OrderInfo::getOrderNo, orderNo)
        //);
        OrderInfo orderInfo = this.getOrderInfo(orderNo);
        String orderStatus = orderInfo.getOrderStatus();
        //极端情况下 选择一：即使延迟关闭订单，最后一刻付款，只要用户付款，将订单改为已支付，进行虚拟物品发货
        //2.更新订单状态:已支付
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
        orderInfoMapper.updateById(orderInfo);
        //选择二：订单延迟关闭，用户付款成功，将订单取消，且给用户进行退款
        /*if(SystemConstant.ORDER_STATUS_CANCEL.equals(orderStatus)){
            //TODO 远程调用支付微服务 更新本地交易记录取消、发起退款，保证一致性
        }*/
        //3.远程调用用户服务虚拟物品发货
        //3.1 创建虚拟物品发货VO对象
        UserPaidRecordVo vo = new UserPaidRecordVo();
        vo.setOrderNo(orderNo);
        vo.setUserId(orderInfo.getUserId());
        vo.setItemType(orderInfo.getItemType());
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        if (CollUtil.isNotEmpty(orderDetailList)) {
            List<Long> itemIdList = orderDetailList.stream().map(OrderDetail::getItemId).collect(Collectors.toList());
            vo.setItemIdList(itemIdList);
            //3.2 发起远程调用
            Result result = userFeignClient.savePaidRecord(vo);
            if (result.getCode().intValue() != 200) {
                throw new GuiguException(result.getCode(), result.getMessage());
            }
        }
    }
}
